/*
* Copyright 2010 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/

package org.etherframework.wire.wirings
{
	import org.etherframework.core.Access;
	import org.etherframework.core.DomainAccess;
	import org.etherframework.core.IBridge;
	import org.etherframework.core.events.InteractionEvent;
	import org.etherframework.reflection.Metadata;
	import org.etherframework.wire.WiringData;
	import org.etherframework.wire.WiringUtil;
	
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	public final class Proxy implements IWiring, IInjection
	{
		public var name:String;
		
		[Inspectable(defaultValue="local", enumeration="local,hierarchical,@hierarchical,hierarchical@,global")]
		public var access:String = "local";
		
		[Inspectable(defaultValue="single", enumeration="single,multiple")]
		public var domain:String = "single";
		
		public var statical:Boolean = false;
		
		private var dictionary:Dictionary;
		
		private var bridge:IBridge;
		
		public function wire(name:String, target:Object, item:Object, data:WiringData):Boolean
		{
			if (!(item is Metadata))
				return false;
			
			var metadata:Metadata = item as Metadata;
			var properties:Dictionary = WiringUtil.getProperties(metadata.properties);
			
			if ((properties["statical"] == "true") != (target is Class))
				return false;
			
			this.name = (properties["name"] || properties["__value"]);
			
			if (this.name)
			{
				bridge = data.bridge;
				dictionary = new Dictionary(true);
				dictionary[target] = item;
				access = Access.getAccess(properties["access"]);
				domain = DomainAccess.getAccess(properties["domain"]);
				statical = (properties["statical"] == "true");
				bridge.listen(InteractionEvent.GET_PROXY + this.name, 
								proxyHandler, access, domain);
				
				var evt:InteractionEvent = new InteractionEvent(InteractionEvent.PROXY_ADDED + this.name);
				evt.data = new ProxyInfo(this.name, target, bridge.token);
				bridge.dispatch(evt, access, domain);
				return true;
			}
			
			return false;
		}
		
		public function unwire(target:Object):Boolean
		{
			var evt:InteractionEvent = new InteractionEvent(InteractionEvent.PROXY_REMOVED + name);
			bridge.dispatch(evt, access, domain);
			
			dictionary = null;
			
			return true;
		}
		
		public function inject(name:String, target:Object, data:WiringData):Boolean
		{
			if (statical != (target is Class))
				return false;
			
			if (this.name)
			{
				bridge = data.bridge;
				dictionary = new Dictionary(true);
				dictionary[target] = true;
				access = Access.getAccess(access);
				domain = DomainAccess.getAccess(domain);
				
				bridge.listen(InteractionEvent.GET_PROXY + this.name, 
								proxyHandler, access, domain);
				
				var evt:InteractionEvent = new InteractionEvent(InteractionEvent.PROXY_ADDED + this.name);
				evt.data = new ProxyInfo(this.name, target, bridge.token);
				bridge.dispatch(evt, access, domain);
				return true;
			}
			
			return false;
		}
		
		private function proxyHandler(event:Event):void
		{
			var target:Object;
			
			if (("result" in event) && 
				(event["result"] is Array) && 
				(target = getTarget(dictionary)))
			{
				event["result"].push(new ProxyInfo(this.name, target, bridge.token));
			}
		}
		
		private function getTarget(dictionary:Dictionary):Object
		{
			for (var target:Object in dictionary){}
			return target;
		}
		
	}
}

class ProxyInfo
{
	function ProxyInfo(name:String, target:Object, token:String)
	{
		_name = name;
		_target = target;
		_token = token;
	}
	
	private var _name:String;

	public function get name():String
	{
		return _name;
	}
	
	private var _target:Object;

	public function get target():Object
	{
		return _target;
	}
	
	private var _token:String;

	public function get token():String
	{
		return _token;
	}
	
}